<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/12/3
 * Time: 21:01
 */

namespace luo;

use luo\exception\ErrorException;

class Error
{

    public static function register(){

        error_reporting(E_ALL);
        set_error_handler([__CLASS__, 'error']);
        set_exception_handler([__CLASS__, 'exception']);
        register_shutdown_function([__CLASS__, 'shutdown']);

    }

    /**
     * @param $errno
     * @param $errstr
     * @param string $errfile
     * @param int $errline
     * @param array $errcontext
     * @return bool
     * @throws ErrorException
     */
    public static function error($errno, $errstr, $errfile = '', $errline = 0, array $errcontext = []){

        if ((error_reporting() & $errno)) {
            throw new ErrorException($errno, $errstr, $errfile, $errline, $errcontext);
        }else{
            return false;
        }

    }

    /**
     * @param $e
     * @throws \ReflectionException
     */
    public static function exception($e){

        if (!$e instanceof \Exception) {

            if ($e instanceof \ParseError) {
                $message  = 'Parse error: ' . $e->getMessage();
                $errno = E_PARSE;
            } elseif ($e instanceof \TypeError) {
                $message  = 'Type error: ' . $e->getMessage();
                $errno = E_RECOVERABLE_ERROR;
            } else {
                $message  = 'Fatal error: ' . $e->getMessage();
                $errno = E_ERROR;
            }
            $exception = new ErrorException($errno, $message, $e->getFile(), $e->getLine());
            $traceReflector = new \ReflectionProperty('Exception', 'trace');
            $traceReflector->setAccessible(true);
            $traceReflector->setValue($exception, $e->getTrace());
            $e = $exception;

        }

        self::toResponse($e)->send();

    }

    /**
     * @throws \ReflectionException
     */
    public static function shutdown(){

        $error = error_get_last();
        if(!is_null($error)){
            $exception = new ErrorException($error['type'], $error['message'], $error['file'], $error['line']);
            self::exception($exception);
        }

    }

    /**
     * @param $exception
     * @return Response
     */
    public static function toResponse($exception){

        if($debug = Config::get('app_debug')){
            $data = [
                'debug'   => $debug,
                'class'   => get_class($exception),
                'file'    => $exception->getFile(),
                'line'    => $exception->getLine(),
                'message' => $exception->getMessage(),
                'code'    => $exception->getCode(),
                'trace'   => $exception->getTraceAsString()
            ];
        }else{
            $data = [
                'debug' => $debug,
                'message' => $exception->getMessage(),
            ];
            if(Config::get('exception_message')){
                $data['message'] = Config::get('exception_message');
            }
        }

        while(ob_get_level() > 1){
            ob_end_clean();
        }
        $data['echo'] = ob_get_clean();

        ob_start();
        extract($data);
        include Config::get('exception_tpl');
        $content = ob_get_clean();
        Log::instance()->write($content, __LINE__, 'exception', __FILE__);
        $response = new Response($content, 500);
        return $response;

    }

}